package org.Yiran.timetale_re.tile.blocktile;

import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.network.PacketDistributor;
import org.Yiran.timetale_re.block.decorate.longtable.LongTable;
import org.Yiran.timetale_re.network.NetworkChannel;
import org.Yiran.timetale_re.network.SCResponsePacket;
import org.Yiran.timetale_re.tile.TileEntityRegister;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nullable;

/**
 * 长桌方块实体类
 * <p>
 * 管理长桌左右两个部分的物品存储、旋转角度和位置偏移。
 * 负责处理物品的放置、移除、数据持久化和网络同步等功能。
 * </p>
 * 
 * @author Yiran
 * @version 1.0
 */
public class LongTableBlockEntity extends BlockEntity {
    
    // ==================== 物品数据字段 ====================
    
    /**
     * 左侧物品
     * <p>
     * 存储放置在长桌左侧的物品，通常用于展示或制作过程中的输入材料。
     * </p>
     */
    public ItemStack leftItem = ItemStack.EMPTY;
    
    /**
     * 右侧物品
     * <p>
     * 存储放置在长桌右侧的物品，通常用于展示或制作过程中的输出结果。
     * </p>
     */
    public ItemStack rightItem = ItemStack.EMPTY;

    // ==================== 物品旋转角度 ====================
    
    /**
     * 左侧物品旋转角度
     * <p>
     * 控制左侧物品在渲染时的旋转角度，用于实现物品的动态展示效果。
     * </p>
     */
    public float leftItemRotation = 0f;
    
    /**
     * 右侧物品旋转角度
     * <p>
     * 控制右侧物品在渲染时的旋转角度，用于实现物品的动态展示效果。
     * </p>
     */
    public float rightItemRotation = 0f;

    // ==================== 物品位置偏移 ====================
    
    /**
     * 左侧物品X轴偏移
     * <p>
     * 控制左侧物品在X轴方向上的位置偏移，用于微调物品的显示位置。
     * </p>
     */
    public float leftItemXOffset = 0f;
    
    /**
     * 左侧物品Z轴偏移
     * <p>
     * 控制左侧物品在Z轴方向上的位置偏移，用于微调物品的显示位置。
     * </p>
     */
    public float leftItemZOffset = 0f;
    
    /**
     * 右侧物品X轴偏移
     * <p>
     * 控制右侧物品在X轴方向上的位置偏移，用于微调物品的显示位置。
     * </p>
     */
    public float rightItemXOffset = 0f;
    
    /**
     * 右侧物品Z轴偏移
     * <p>
     * 控制右侧物品在Z轴方向上的位置偏移，用于微调物品的显示位置。
     * </p>
     */
    public float rightItemZOffset = 0f;

    // ==================== 状态标记 ====================
    
    /**
     * 标记是否需要同步
     * <p>
     * 用于标记方块实体的数据是否发生了变化，需要同步到客户端。
     * </p>
     */
    private boolean needsSync = false;

    // ==================== 构造函数 ====================

    /**
     * 构造函数
     * <p>
     * 初始化长桌方块实体，注册到指定的方块实体类型。
     * </p>
     * 
     * @param pos 方块在世界中的位置坐标
     * @param state 方块的状态对象
     */
    public LongTableBlockEntity(BlockPos pos, BlockState state) {
        super(TileEntityRegister.LONG_TABLE_BLOCK_ENTITY.get(), pos, state);
    }

    // ==================== 物品设置相关方法 ====================

    /**
     * 设置指定部分的物品（完整参数版本）
     * <p>
     * 在长桌的指定部分设置物品，并配置其旋转角度和位置偏移。
     * 同时会发送网络同步包到客户端，确保客户端显示与服务端一致。
     * </p>
     * 
     * @param partId 部件ID (LEFT_PART 或 RIGHT_PART)
     * @param stack 物品堆
     * @param rotation 旋转角度
     * @param xOffset X轴偏移
     * @param zOffset Z轴偏移
     * @param isCarving 是否正在雕刻
     * @return 是否设置成功
     */
    public boolean setItem(int partId, ItemStack stack, float rotation, float xOffset, float zOffset, boolean isCarving) {
        if (partId == LongTable.LEFT_PART || partId == LongTable.RIGHT_PART) {
            updateItemData(partId, stack, rotation, xOffset, zOffset);

            // 发送带雕刻标记的同步包
            if (level != null && !level.isClientSide) {
                sendItemSyncPacket(partId, stack, rotation, xOffset, zOffset, isCarving);
            }
            return true;
        }
        return false;
    }

    /**
     * 更新物品数据的核心方法
     * <p>
     * 根据部件ID更新对应部分的物品数据，包括物品本身、旋转角度和位置偏移。
     * 更新完成后会标记实体已更改并发送同步包到客户端。
     * </p>
     * 
     * @param partId 部件ID
     * @param stack 物品堆
     * @param rotation 旋转角度
     * @param xOffset X轴偏移
     * @param zOffset Z轴偏移
     */
    private void updateItemData(int partId, ItemStack stack, float rotation, float xOffset, float zOffset) {
        // 根据部件ID设置对应的数据
        if (partId == LongTable.LEFT_PART) {
            updateLeftItemData(stack, rotation, xOffset, zOffset);
        } else if (partId == LongTable.RIGHT_PART) {
            updateRightItemData(stack, rotation, xOffset, zOffset);
        }

        // 标记需要同步并触发变化
        this.needsSync = true;
        setChanged();

        // 发送同步包到客户端
        if (level != null && !level.isClientSide) {
            sendItemSyncPacket(partId, stack, rotation, xOffset, zOffset, false);
        }
    }

    /**
     * 更新左侧物品数据
     * <p>
     * 更新左侧物品的相关数据，包括物品本身、旋转角度和位置偏移。
     * </p>
     * 
     * @param stack 物品堆
     * @param rotation 旋转角度
     * @param xOffset X轴偏移
     * @param zOffset Z轴偏移
     */
    private void updateLeftItemData(ItemStack stack, float rotation, float xOffset, float zOffset) {
        this.leftItem = stack.copy();
        this.leftItem.setCount(1);
        this.leftItemRotation = rotation;
        this.leftItemXOffset = xOffset;
        this.leftItemZOffset = zOffset;
    }

    /**
     * 更新右侧物品数据
     * <p>
     * 更新右侧物品的相关数据，包括物品本身、旋转角度和位置偏移。
     * </p>
     * 
     * @param stack 物品堆
     * @param rotation 旋转角度
     * @param xOffset X轴偏移
     * @param zOffset Z轴偏移
     */
    private void updateRightItemData(ItemStack stack, float rotation, float xOffset, float zOffset) {
        this.rightItem = stack.copy();
        this.rightItem.setCount(1);
        this.rightItemRotation = rotation;
        this.rightItemXOffset = xOffset;
        this.rightItemZOffset = zOffset;
    }

    /**
     * 发送物品同步网络包
     * <p>
     * 将物品数据变化通过网络同步到跟踪该方块的客户端，确保客户端显示与服务端一致。
     * </p>
     * 
     * @param partId 部件ID
     * @param stack 物品堆
     * @param rotation 旋转角度
     * @param xOffset X轴偏移
     * @param zOffset Z轴偏移
     * @param isCarving 是否正在雕刻
     */
    private void sendItemSyncPacket(int partId, ItemStack stack, float rotation, float xOffset, float zOffset, boolean isCarving) {
        NetworkChannel.CHANNEL.send(
                PacketDistributor.TRACKING_CHUNK.with(() -> {
                    if (level != null) {
                        return level.getChunkAt(worldPosition);
                    }
                    return null;
                }),
                new SCResponsePacket.LongTableItemSyncPacket(worldPosition, partId, stack, rotation, xOffset, zOffset, isCarving)
        );
    }

    // ==================== 物品设置方法重载 ====================

    /**
     * 设置指定部分的物品（不指定雕刻状态）
     * <p>
     * 在长桌的指定部分设置物品，并配置其旋转角度和位置偏移。
     * 默认不处于雕刻状态。
     * </p>
     * 
     * @param partId 部件ID
     * @param stack 物品堆
     * @param rotation 旋转角度
     * @param xOffset X轴偏移
     * @param zOffset Z轴偏移
     * @return 是否设置成功
     */
    public boolean setItem(int partId, ItemStack stack, float rotation, float xOffset, float zOffset) {
        return setItem(partId, stack, rotation, xOffset, zOffset, false);
    }

    /**
     * 设置指定部分的物品（仅指定物品）
     * <p>
     * 在长桌的指定部分设置物品，使用默认的旋转角度和位置偏移。
     * </p>
     * 
     * @param partId 部件ID
     * @param stack 物品堆
     * @return 是否设置成功
     */
    public boolean setItem(int partId, ItemStack stack) {
        return setItem(partId, stack, 0f);
    }

    /**
     * 设置指定部分的物品（指定物品和旋转角度）
     * <p>
     * 在长桌的指定部分设置物品，并指定旋转角度，位置偏移使用默认值。
     * </p>
     * 
     * @param partId 部件ID
     * @param stack 物品堆
     * @param rotation 旋转角度
     * @return 是否设置成功
     */
    public boolean setItem(int partId, ItemStack stack, float rotation) {
        return setItem(partId, stack, rotation, 0f, 0f);
    }

    // ==================== 物品获取相关方法 ====================

    /**
     * 获取指定部分的物品
     * <p>
     * 获取长桌指定部分的物品，如果部件ID无效则返回空物品。
     * </p>
     * 
     * @param partId 部件ID
     * @return 物品堆
     */
    public ItemStack getItem(int partId) {
        if (partId == LongTable.LEFT_PART) {
            return leftItem;
        } else if (partId == LongTable.RIGHT_PART) {
            return rightItem;
        }
        return ItemStack.EMPTY;
    }

    /**
     * 获取指定部分的物品旋转角度
     * <p>
     * 获取长桌指定部分物品的旋转角度，如果部件ID无效则返回0。
     * </p>
     * 
     * @param partId 部件ID
     * @return 旋转角度
     */
    public float getItemRotation(int partId) {
        if (partId == LongTable.LEFT_PART) {
            return leftItemRotation;
        } else if (partId == LongTable.RIGHT_PART) {
            return rightItemRotation;
        }
        return 0f;
    }

    /**
     * 获取指定部分的物品X轴偏移
     * <p>
     * 获取长桌指定部分物品的X轴偏移量，如果部件ID无效则返回0。
     * </p>
     * 
     * @param partId 部件ID
     * @return X轴偏移
     */
    public float getItemXOffset(int partId) {
        if (partId == LongTable.LEFT_PART) {
            return leftItemXOffset;
        } else if (partId == LongTable.RIGHT_PART) {
            return rightItemXOffset;
        }
        return 0f;
    }

    /**
     * 获取指定部分的物品Z轴偏移
     * <p>
     * 获取长桌指定部分物品的Z轴偏移量，如果部件ID无效则返回0。
     * </p>
     * 
     * @param partId 部件ID
     * @return Z轴偏移
     */
    public float getItemZOffset(int partId) {
        if (partId == LongTable.LEFT_PART) {
            return leftItemZOffset;
        } else if (partId == LongTable.RIGHT_PART) {
            return rightItemZOffset;
        }
        return 0f;
    }

    // ==================== 物品移除相关方法 ====================

    /**
     * 移除指定部分的物品
     * <p>
     * 从长桌的指定部分移除物品，并发送同步包到客户端。
     * </p>
     * 
     * @param partId 部件ID
     * @return 被移除的物品堆
     */
    public ItemStack removeItem(int partId) {
        ItemStack stack = handleItemRemoval(partId);

        if (!stack.isEmpty()) {
            setChanged();
            // 发送同步包到客户端
            if (level != null && !level.isClientSide) {
                NetworkChannel.CHANNEL.send(
                        PacketDistributor.TRACKING_CHUNK.with(() -> level.getChunkAt(worldPosition)),
                        new SCResponsePacket.LongTableItemSyncPacket(worldPosition, partId)
                );
            }
        }
        return stack;
    }

    /**
     * 处理物品移除的核心逻辑
     * <p>
     * 根据部件ID调用对应的物品移除方法。
     * </p>
     * 
     * @param partId 部件ID
     * @return 被移除的物品堆
     */
    private ItemStack handleItemRemoval(int partId) {
        if (partId == LongTable.LEFT_PART) {
            return removeLeftItem();
        } else if (partId == LongTable.RIGHT_PART) {
            return removeRightItem();
        }
        return ItemStack.EMPTY;
    }

    /**
     * 移除左侧物品
     * <p>
     * 移除左侧的物品，并重置相关的位置偏移数据。
     * </p>
     * 
     * @return 被移除的物品堆
     */
    private ItemStack removeLeftItem() {
        if (this.leftItem.isEmpty()) {
            return ItemStack.EMPTY; // 如果没有物品，直接返回空
        }
        ItemStack stack = leftItem.copy();
        leftItem = ItemStack.EMPTY;
        // 重置位置偏移
        leftItemXOffset = 0f;
        leftItemZOffset = 0f;
        this.needsSync = true;
        return stack;
    }

    /**
     * 移除右侧物品
     * <p>
     * 移除右侧的物品，并重置相关的位置偏移数据。
     * </p>
     * 
     * @return 被移除的物品堆
     */
    private ItemStack removeRightItem() {
        if (this.rightItem.isEmpty()) {
            return ItemStack.EMPTY; // 如果没有物品，直接返回空
        }
        ItemStack stack = rightItem.copy();
        rightItem = ItemStack.EMPTY;
        // 重置位置偏移
        rightItemXOffset = 0f;
        rightItemZOffset = 0f;
        this.needsSync = true;
        return stack;
    }

    // ==================== NBT数据序列化相关方法 ====================

    /**
     * 从NBT标签加载数据
     * <p>
     * 从NBT标签中加载长桌方块实体的所有数据，包括物品、旋转角度和位置偏移。
     * </p>
     * 
     * @param tag NBT标签
     */
    @Override
    public void load(@NotNull CompoundTag tag) {
        super.load(tag);
        loadItemData(tag);
        loadItemRotationData(tag);
        loadItemOffsetData(tag);
    }

    /**
     * 加载物品数据
     * <p>
     * 从NBT标签中加载左右两侧的物品数据。
     * </p>
     * 
     * @param tag NBT标签
     */
    private void loadItemData(CompoundTag tag) {
        if (tag.contains("LeftItem")) {
            this.leftItem = ItemStack.of(tag.getCompound("LeftItem"));
        } else {
            this.leftItem = ItemStack.EMPTY;
        }

        if (tag.contains("RightItem")) {
            this.rightItem = ItemStack.of(tag.getCompound("RightItem"));
        } else {
            this.rightItem = ItemStack.EMPTY;
        }
    }

    /**
     * 加载物品旋转角度数据
     * <p>
     * 从NBT标签中加载左右两侧物品的旋转角度数据。
     * </p>
     * 
     * @param tag NBT标签
     */
    private void loadItemRotationData(CompoundTag tag) {
        this.leftItemRotation = tag.getFloat("LeftItemRotation");
        this.rightItemRotation = tag.getFloat("RightItemRotation");
    }

    /**
     * 加载物品位置偏移数据
     * <p>
     * 从NBT标签中加载左右两侧物品的位置偏移数据。
     * </p>
     * 
     * @param tag NBT标签
     */
    private void loadItemOffsetData(CompoundTag tag) {
        this.leftItemXOffset = tag.getFloat("LeftItemXOffset");
        this.leftItemZOffset = tag.getFloat("LeftItemZOffset");
        this.rightItemXOffset = tag.getFloat("RightItemXOffset");
        this.rightItemZOffset = tag.getFloat("RightItemZOffset");
    }

    /**
     * 保存额外数据到NBT标签
     * <p>
     * 将长桌方块实体的所有数据保存到NBT标签中，包括物品、旋转角度和位置偏移。
     * </p>
     * 
     * @param tag NBT标签
     */
    @Override
    protected void saveAdditional(@NotNull CompoundTag tag) {
        super.saveAdditional(tag);
        saveItemData(tag);
        saveItemRotationData(tag);
        saveItemOffsetData(tag);
    }

    /**
     * 保存物品数据
     * <p>
     * 将左右两侧的物品数据保存到NBT标签中。
     * </p>
     * 
     * @param tag NBT标签
     */
    private void saveItemData(CompoundTag tag) {
        if (!leftItem.isEmpty()) {
            CompoundTag leftItemTag = new CompoundTag();
            leftItem.save(leftItemTag);
            tag.put("LeftItem", leftItemTag);
        }

        if (!rightItem.isEmpty()) {
            CompoundTag rightItemTag = new CompoundTag();
            rightItem.save(rightItemTag);
            tag.put("RightItem", rightItemTag);
        }
    }

    /**
     * 保存物品旋转角度数据
     * <p>
     * 将左右两侧物品的旋转角度数据保存到NBT标签中。
     * </p>
     * 
     * @param tag NBT标签
     */
    private void saveItemRotationData(CompoundTag tag) {
        tag.putFloat("LeftItemRotation", leftItemRotation);
        tag.putFloat("RightItemRotation", rightItemRotation);
    }

    /**
     * 保存物品位置偏移数据
     * <p>
     * 将左右两侧物品的位置偏移数据保存到NBT标签中。
     * </p>
     * 
     * @param tag NBT标签
     */
    private void saveItemOffsetData(CompoundTag tag) {
        tag.putFloat("LeftItemXOffset", leftItemXOffset);
        tag.putFloat("LeftItemZOffset", leftItemZOffset);
        tag.putFloat("RightItemXOffset", rightItemXOffset);
        tag.putFloat("RightItemZOffset", rightItemZOffset);
    }

    // ==================== 网络同步相关方法 ====================

    /**
     * 获取更新标签（用于网络同步）
     * <p>
     * 获取用于网络同步的NBT标签，包含所有需要同步的数据。
     * </p>
     * 
     * @return NBT标签
     */
    @Override
    public @NotNull CompoundTag getUpdateTag() {
        CompoundTag tag = super.getUpdateTag();
        saveAdditional(tag);
        return tag;
    }

    /**
     * 获取更新数据包（用于网络同步）
     * <p>
     * 获取用于网络同步的数据包，确保客户端和服务端数据一致。
     * </p>
     * 
     * @return 客户端绑定的方块实体数据包
     */
    @Nullable
    @Override
    public ClientboundBlockEntityDataPacket getUpdatePacket() {
        return ClientboundBlockEntityDataPacket.create(this);
    }
}